package project.bitmap.service;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
public class SignServiceImpl implements SignService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public Object doSign(Long userId, String dateStr) {
        String bitKey = buildSignKey(100000L, LocalDate.now());
        //当前签到情况
        LocalDate date = LocalDate.now();
        redisTemplate.delete(bitKey);

        //offset是从0开始的，因此2号签到，offset要标记为1
        int todayOffset = date.getDayOfMonth() - 1;
        redisTemplate.opsForValue().setBit(bitKey, 3, true);  //4号签到
        redisTemplate.opsForValue().setBit(bitKey, 4, true);  //5号签到
        redisTemplate.opsForValue().setBit(bitKey, 5, true);  //6号签到
        redisTemplate.opsForValue().setBit(bitKey, 6, true);  //7号签到
        redisTemplate.opsForValue().setBit(bitKey, 7, true);  //8号签到
        redisTemplate.opsForValue().setBit(bitKey, 9, true);  //10号签到
        redisTemplate.opsForValue().setBit(bitKey, todayOffset, true);  //当天签到


        //当月-累计签到
        Long count = (Long) redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(bitKey.getBytes()));
        System.out.println("当月-累计签到：" + count);

        // 当月-首次签到日期
        long bitPosition = (Long) redisTemplate.execute((RedisCallback) cbk -> cbk.bitPos(bitKey.getBytes(), true)) + 1;
        System.out.println("当月-首签日期：" + bitPosition);


        //LinkedHashMap 有序Map
        Map<Object, Object> signMap = new LinkedHashMap<>(date.getDayOfMonth());
        List<Long> list = redisTemplate.opsForValue().bitField(bitKey, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType
                .unsigned(date.lengthOfMonth())).valueAt(0));
        if (null != list && list.size() > 0) {
            //由低位到高位，为0表示未签 为1表示已签
            long v = list.get(0) == null ? 0 : list.get(0);
            // 例如这个月一共31天，那么取到的二进制就是31个的0或1的组合
            System.out.println("签到二进制位输出：" + Long.toBinaryString(v));
            for (int i = date.lengthOfMonth(); i > 0; i--) {
                LocalDate d = date.withDayOfMonth(i);
                // 按位操作向右位移1，再移动回来，再移回来时是用0补位，如果补位后相等说明这个位本来就是0，所以这里判断是不相等，不相等即为1，说明签到了
                signMap.put(formatDate(d, "yyyy-MM-dd"), v >> 1 << 1 != v);
                // 是右移后赋值，意思是向右移动一位出去，此时变量v的值将发生变化
                v >>= 1;
            }
        }
        System.out.println("当月签到情况：" + JSON.toJSONString(signMap, true));


        int signCount = 0;
        boolean isBreak = false;
        //定义一个死循环，有断签就结束循环
        while (true){
            if (isBreak) break;
            list = redisTemplate.opsForValue().bitField(bitKey, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(date.getDayOfMonth())).valueAt(0));
            if (list != null && list.size() >0){
                long v= list.get(0) == null ? 0 :list.get(0);
                for (int i =0;i<date.getDayOfMonth();i++){
                    if (v >> 1 << 1 == v){
                        if (i >0){
                            isBreak = true;
                            break;
                        }
                    }else {
                        signCount +=1;
                    }

                    v>>=1;
                }
                date = date.minusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
            }

        }
        System.out.println("累计签到： "+signCount); //这个累计就是从当天开始往前推

        //不同用法，下面更直观，获取1bit下标手动指定
        BitFieldSubCommands commands = BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(1)).valueAt(3)
                .get(BitFieldSubCommands.BitFieldType.unsigned(1)).valueAt(4)
                .get(BitFieldSubCommands.BitFieldType.unsigned(1)).valueAt(5)
                .get(BitFieldSubCommands.BitFieldType.unsigned(1)).valueAt(6)
                .get(BitFieldSubCommands.BitFieldType.unsigned(1)).valueAt(7)
                .get(BitFieldSubCommands.BitFieldType.unsigned(1)).valueAt(todayOffset);

        list = redisTemplate.opsForValue().bitField(bitKey,commands);

        System.out.println("固定区间取值："+JSON.toJSONString(list,true));

        System.out.println("今天是否签到:"+redisTemplate.opsForValue().getBit(bitKey,todayOffset));

        return null;
    }

    private static final String USER_SIGN = "USER_SIGN:%d%s";

    private static String buildSignKey(Long uid, LocalDate date) {
        return String.format(USER_SIGN, uid, formatDate(date));
    }

    private static String formatDate(LocalDate date, String pattern) {
        return date.format(DateTimeFormatter.ofPattern(pattern));
    }

    private static String formatDate(LocalDate date) {
        return formatDate(date, "yyyyMM");
    }
}
